Spotting differences

Adam Shen

January 6, 2026

Motivation

  • When refactoring, testing, or debugging code, we often want to check that our new results match the old results.

  • When differences exist, we may also be interested in where and how they differ.

The {waldo} package

# install.packages("pak")
pak::pak("waldo")


library(waldo)
  • Inspired by the Waldo books where one needs to find Waldo among many Waldo-lookalikes.
  • Finding Waldo is quite similar to when you're trying to check if your new results match the old results.

waldo::compare()

The main function of the {waldo} package is compare(). Differences between objects are coloured. Quickly running through the examples from the website:

  • Deletion

    compare(c("a", "b", "c"), c("a", "b"))
    `old`: "a" "b" "c"
    `new`: "a" "b"    
    
  • Addition

    compare(c("a", "b"), c("a", "b", "c"))
    `old`: "a" "b"    
    `new`: "a" "b" "c"
    
  • Change

    compare(c("a", "b", "c"), c("a", "B", "c"))
    `old`: "a" "b" "c"
    `new`: "a" "B" "c"
    

Comparing data frames

Let's see how it looks when we compare data frames.

library(tidyverse)

Add an id column to rock data set

Let's take the rock data set from base-R and add an id column so that we can easily update rows by row number.

rock <- datasets::rock %>%
  as_tibble() %>%
  mutate(
    across(everything(), as.double),
    id = 1:n()
  ) %>%
  relocate(id)

rock
# A tibble: 48 × 5
      id  area  peri  shape  perm
   <int> <dbl> <dbl>  <dbl> <dbl>
 1     1  4990 2792. 0.0903   6.3
 2     2  7002 3893. 0.149    6.3
 3     3  7558 3931. 0.183    6.3
 4     4  7352 3869. 0.117    6.3
 5     5  7943 3949. 0.122   17.1
 6     6  7979 4010. 0.167   17.1
 7     7  9333 4346. 0.190   17.1
 8     8  8209 4345. 0.164   17.1
 9     9  8393 3682. 0.204  119  
10    10  6425 3099. 0.162  119  
# ℹ 38 more rows

Create some changes - 1

For the first change, take the fourth row and make the area value NA:

change1 <- rock %>%
  slice(4) %>%
  mutate(area = NA)

change1
# A tibble: 1 × 5
     id area   peri shape  perm
  <int> <lgl> <dbl> <dbl> <dbl>
1     4 NA    3869. 0.117   6.3

View the changes - 1

rock1 <- rock %>%
  rows_update(change1, by = "id")

rock1
# A tibble: 48 × 5
      id  area  peri  shape  perm
   <int> <dbl> <dbl>  <dbl> <dbl>
 1     1  4990 2792. 0.0903   6.3
 2     2  7002 3893. 0.149    6.3
 3     3  7558 3931. 0.183    6.3
 4     4    NA 3869. 0.117    6.3
 5     5  7943 3949. 0.122   17.1
 6     6  7979 4010. 0.167   17.1
 7     7  9333 4346. 0.190   17.1
 8     8  8209 4345. 0.164   17.1
 9     9  8393 3682. 0.204  119  
10    10  6425 3099. 0.162  119  
# ℹ 38 more rows

Comparison - 1

compare(rock, rock1)
old vs new
             area
  old[1, ]   4990
  old[2, ]   7002
  old[3, ]   7558
- old[4, ]   7352
+ new[4, ]     NA
  old[5, ]   7943
  old[6, ]   7979
  old[7, ]   9333

`old$area[1:7]`: 4990.0 7002.0 7558.0 7352.0 7943.0 7979.0 9333.0
`new$area[1:7]`: 4990.0 7002.0 7558.0     NA 7943.0 7979.0 9333.0

Create some changes - 2

For the second change, take rows 8, 10, and 11, and multiply all values (except id) by 1.10.

change2 <- rock %>%
  slice(c(8, 10, 11)) %>%
  mutate(
    across(
      -id,
      \(x) x * 1.10
    )
  )

change2
# A tibble: 3 × 5
     id   area  peri shape  perm
  <int>  <dbl> <dbl> <dbl> <dbl>
1     8  9030. 4779. 0.181  18.8
2    10  7068. 3409. 0.179 131. 
3    11 10300. 4928. 0.166 131. 

View the changes - 2

rock2 <- rock %>%
  rows_update(change2, by = "id")

rock2
# A tibble: 48 × 5
      id  area  peri  shape  perm
   <int> <dbl> <dbl>  <dbl> <dbl>
 1     1 4990  2792. 0.0903   6.3
 2     2 7002  3893. 0.149    6.3
 3     3 7558  3931. 0.183    6.3
 4     4 7352  3869. 0.117    6.3
 5     5 7943  3949. 0.122   17.1
 6     6 7979  4010. 0.167   17.1
 7     7 9333  4346. 0.190   17.1
 8     8 9030. 4779. 0.181   18.8
 9     9 8393  3682. 0.204  119  
10    10 7068. 3409. 0.179  131. 
# ℹ 38 more rows

Comparison - 2

compare(rock, rock2)
old vs new
               area     peri     shape    perm
  old[5, ]   7943.0 3948.540 0.1224170   17.10
  old[6, ]   7979.0 4010.150 0.1670450   17.10
  old[7, ]   9333.0 4345.750 0.1896510   17.10
- old[8, ]   8209.0 4344.750 0.1641270   17.10
+ new[8, ]   9029.9 4779.225 0.1805397   18.81
  old[9, ]   8393.0 3682.040 0.2036540  119.00
- old[10, ]  6425.0 3098.650 0.1623940  119.00
+ new[10, ]  7067.5 3408.515 0.1786334  130.90
- old[11, ]  9364.0 4480.050 0.1509440  119.00
+ new[11, ] 10300.4 4928.055 0.1660384  130.90
  old[12, ]  8624.0 3986.240 0.1481410  119.00
  old[13, ] 10651.0 4036.540 0.2285950   82.40
  old[14, ]  8868.0 3518.040 0.2316230   82.40

`old$area[5:14]`: 7943.0 7979.0 9333.0 8209.0 8393.0 6425.0  9364.0 8624.0 10651.0 8868.0
`new$area[5:14]`: 7943.0 7979.0 9333.0 9029.9 8393.0 7067.5 10300.4 8624.0 10651.0 8868.0

`old$peri[5:14]`: 3948.5 4010.2 4345.8 4344.8 3682.0 3098.7 4480.1 3986.2 4036.5 3518.0
`new$peri[5:14]`: 3948.5 4010.2 4345.8 4779.2 3682.0 3408.5 4928.1 3986.2 4036.5 3518.0

`old$shape[5:14]`: 0.122 0.167 0.190 0.164 0.204 0.162 0.151 0.148 0.229 0.232
`new$shape[5:14]`: 0.122 0.167 0.190 0.181 0.204 0.179 0.166 0.148 0.229 0.232

`old$perm[5:14]`: 17.1 17.1 17.1 17.1 119.0 119.0 119.0 119.0 82.4 82.4
`new$perm[5:14]`: 17.1 17.1 17.1 18.8 119.0 130.9 130.9 119.0 82.4 82.4

Summary

  • Use waldo::compare() to easily compare changes between objects.

  • Note: waldo::compare() works for vectors, data frames, lists, and more!